<template>
  <fe-page :title="title" @back="goBack">
		<div class="form-container">
      <div class="left-board">
        <div class="left-header">组件库</div>
        <el-scrollbar height="calc(100% - 41px)">
          <div class="components-list">
            <div class="components-title">
              <svg-icon icon-class="component" />输入型组件
            </div>
            <draggable class="components-draggable" :list="inputComponents"
              :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
              draggable=".components-item" :sort="false" @end="onEnd" item-key="label">
              <template #item="{ element, index }">
                <div :key="index" class="components-item" @click="addComponent(element)">
                  <div class="components-body">
                    <svg-icon :icon-class="element.tagIcon" />
                    {{ element.label }}
                  </div>
                </div>
              </template>
            </draggable>
            <div class="components-title">
              <svg-icon icon-class="component" />选择型组件
            </div>
            <draggable class="components-draggable" :list="selectComponents"
              :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
              draggable=".components-item" :sort="false" @end="onEnd" item-key="label">
              <template #item="{ element, index }">
                <div :key="index" class="components-item" @click="addComponent(element)">
                  <div class="components-body">
                    <svg-icon :icon-class="element.tagIcon" />
                    {{ element.label }}
                  </div>
                </div>
              </template>
            </draggable>
            <div class="components-title">
              <svg-icon icon-class="component" /> 布局型组件
            </div>
            <draggable class="components-draggable" :list="layoutComponents"
              :group="{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
              draggable=".components-item" :sort="false" @end="onEnd" item-key="label">
              <template #item="{ element, index }">
                <div :key="index" class="components-item" @click="addComponent(element)">
                  <div class="components-body">
                    <svg-icon :icon-class="element.tagIcon" />
                    {{ element.label }}
                  </div>
                </div>
              </template>
            </draggable>
          </div>
        </el-scrollbar>
      </div>
      <div class="center-board">
        <div class="center-header">
          <el-button-group>
            <el-tooltip effect="dark" content="下载vue文件" placement="top">
              <el-button type="primary" icon="Download" @click="handleDownload"></el-button>
            </el-tooltip>
            <el-tooltip effect="dark" content="复制代码" placement="top">
              <el-button type="primary" icon="DocumentCopy" @click="handleCopy"></el-button>
            </el-tooltip>
            <el-tooltip effect="dark" content="预览" placement="top">
              <el-button type="primary" icon="View" @click="handleView"></el-button>
            </el-tooltip>
            <el-tooltip effect="dark" content="清空" placement="top">
              <el-button type="danger" icon="Delete" @click="handleEmpty"></el-button>
            </el-tooltip>
          </el-button-group>
        </div>
        <el-scrollbar height="calc(100% - 41px)" wrap-class="drag-box">
          <el-form :size="formConf.size" :label-position="formConf.labelPosition" :disabled="formConf.disabled"
            :label-width="formConf.labelWidth + 'px'">
            <draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup"
              item-key="label">
              <template #item="{ element, index }">
                <draggable-item :key="element.renderKey" :drawing-list="drawingList" :element="element" :index="index"
                  :active-id="activeId" :form-conf="formConf" @activeItem="activeFormItem" @copyItem="drawingItemCopy"
                  @deleteItem="drawingItemDelete" />
              </template>
            </draggable>
            <div v-show="!drawingList.length" class="empty-info">
              从左侧拖入或点选组件进行表单设计
            </div>
          </el-form>
        </el-scrollbar>
      </div>
      <right-panel :active-data="activeData" :form-conf="formConf" :show-field="!!drawingList.length"
        @tag-change="tagChange" />

      <code-type-dialog v-model="dialogVisible" title="选择生成类型" :showFileName="showFileName" @confirm="generate" />
      <el-dialog title="预览表单" v-model="previewOpen" width="800px" append-to-body>
        <preview-form :formConf="previewData.formConf" :drawingList="previewData.drawingList" type="preview" v-if="previewOpen"></preview-form>
      </el-dialog>
      <input id="copyNode" type="hidden">
    </div>
		<template #extra>
			<el-button type="primary" @click="submitForm" :loading="loading">保 存</el-button>
		</template>
  </fe-page>
</template>

<script setup name="FormAdd">
import draggable from "vuedraggable/dist/vuedraggable.common"
import ClipboardJS from 'clipboard'
import beautifier from 'js-beautify'
import { inputComponents, selectComponents, layoutComponents, formConf as formConfData } from '@/utils/generator/config'
import { beautifierConf } from '@/utils/index'
import drawingDefalut from '@/utils/generator/drawingDefalut'
import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/utils/generator/html'
import { makeUpJs, buildAttributesObj } from '@/utils/generator/js'
import { makeUpCss } from '@/utils/generator/css'
import { generateGUID } from "@/utils/ruoyi"
import Download from '@/plugins/download'
import { ElNotification } from 'element-plus'
import DraggableItem from './build/DraggableItem'
import RightPanel from './build/RightPanel'
import CodeTypeDialog from './build/CodeTypeDialog'
import previewForm from './build/index.vue'
import { getData, addProcess, updateProcess } from "@/api/service/process"

const route = useRoute()
const { proxy } = getCurrentInstance()

const loading = ref(false)
const id = route.params && route.params.id
const title = id? '编辑表单' : '新增表单'
const form = ref({})
const drawingList = ref([])
const dialogVisible = ref(false)
const showFileName = ref(false)
const operationType = ref('')
const idGlobal = ref(100)
const activeData = ref({})
const activeId = ref(0)
const generateConf = ref(null)
const formData = ref({})
const formConf = ref({})
const previewOpen = ref(false)
const previewData = ref({})
let oldActiveId
let tempActiveData

function getInfo() {
	loading.value = true
	getData(id).then(response => {
		loading.value = false
		let result = response.result || ''
    form.value = result
    let content = JSON.parse(result)
    formConf.value = content.formParams
    drawingList.value = content.drawingParams
    activeData.value = drawingList.value[0] || {}
    activeId.value = activeData.value.formId || 0
  })
}

function activeFormItem(element) {
  activeData.value = element
  activeId.value = element.formId
}
function handleCopy() {
  dialogVisible.value = true
  showFileName.value = false
  operationType.value = 'copy'
}
function handleDownload() {
  dialogVisible.value = true
  showFileName.value = true
  operationType.value = 'download'
}
function handleView() {
  previewOpen.value = true
  let params = dealData()
  previewData.value = {
    formConf: params.formParams,
    drawingList: params.drawingParams
  }
}
function handleEmpty() {
  proxy.$modal.confirm('确定要清空所有组件吗？', '提示', { type: 'warning' }).then(() => {
      idGlobal.value = 100
      drawingList.value = []
    }
  )
}

function onEnd(obj, a) {
  if (obj.from !== obj.to) {
    activeData.value = tempActiveData
    activeId.value = idGlobal.value
  }
}

function addComponent(item) {
  const clone = cloneComponent(item)
  drawingList.value.push(clone)
  activeFormItem(clone)
}

function cloneComponent(origin) {
  idGlobal.value++
  const clone = JSON.parse(JSON.stringify(origin))
  clone.formId = generateGUID()
  clone.span = formConf.value.span
  clone.renderKey = +new Date() // 改变renderKey后可以实现强制更新组件
  if (!clone.layout) clone.layout = 'colFormItem'
  if (clone.layout === 'colFormItem') {
    clone.vModel = `field${idGlobal.value}`
    clone.placeholder !== undefined && (clone.placeholder += clone.label)
    tempActiveData = clone
  } else if (clone.layout === 'rowFormItem') {
    delete clone.label
    clone.componentName = `row${idGlobal.value}`
    clone.gutter = formConf.value.gutter
    tempActiveData = clone
  }
  return tempActiveData
}

function drawingItemCopy(item, parent) {
  let clone = JSON.parse(JSON.stringify(item))
  clone = createIdAndKey(clone)
  parent.push(clone)
  activeFormItem(clone)
}


function createIdAndKey(item) {
  item.formId = ++idGlobal.value
  item.renderKey = +new Date()
  if (item.layout === 'colFormItem') {
    item.vModel = `field${idGlobal.value}`
  } else if (item.layout === 'rowFormItem') {
    item.componentName = `row${idGlobal.value}`
  }
  if (Array.isArray(item.children)) {
    item.children = item.children.map(childItem => createIdAndKey(childItem))
  }
  return item
}

function drawingItemDelete(index, parent) {
  parent.splice(index, 1)
  nextTick(() => {
    const len = drawingList.value.length
    if (len) {
      activeFormItem(drawingList.value[len - 1])
    }
  })
}

function tagChange(newTag) {
  newTag = cloneComponent(newTag)
  newTag.vModel = activeData.value.vModel
  newTag.formId = activeId.value
  newTag.span = activeData.value.span
  delete activeData.value.tag
  delete activeData.value.tagIcon
  delete activeData.value.document
  Object.keys(newTag).forEach(key => {
    if (activeData.value[key] !== undefined
      && typeof activeData.value[key] === typeof newTag[key]) {
      newTag[key] = activeData.value[key]
    }
  })
  activeData.value = newTag
  updateDrawingList(newTag, drawingList.value)
}


function updateDrawingList(newTag, list) {
  const index = list.findIndex(item => item.formId === activeId.value)
  if (index > -1) {
    list.splice(index, 1, newTag)
  } else {
    list.forEach(item => {
      if (Array.isArray(item.children)) updateDrawingList(newTag, item.children)
    })
  }
}
function generate(data) {
  generateConf.value = data
  nextTick(() => {
    switch (operationType.value) {
      case 'copy':
        execCopy(data)
        break
      case 'download':
        execDownload(data)
        break
      default:
        break
    }
  })
}

function execDownload(data) {
  const codeStr = generateCode()
  const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
  Download.saveAs(blob, data.fileName)
}

function execCopy(data) {
  document.getElementById('copyNode').click()
}
function AssembleFormData() {
  formData.value = { fields: JSON.parse(JSON.stringify(drawingList.value)), ...formConf.value }
}
function generateCode() {
  const { type } = generateConf.value
  AssembleFormData()
  const script = vueScript(makeUpJs(formData.value, type))
  const html = vueTemplate(makeUpHtml(formData.value, type))
  const css = cssStyle(makeUpCss(formData.value))
  return beautifier.html(html + script + css, beautifierConf.html)
}
function dealData() {
  let formParams = JSON.parse(JSON.stringify(formConf.value))
  let drawingParams = JSON.parse(JSON.stringify(drawingList.value))
  let dataObj = {}
  let ruleObj = {}
  drawingParams.forEach(el => {
    buildAttributesObj(el, dataObj, ruleObj)
  })
  formParams.formModel = dataObj
  formParams.formRules = ruleObj
  return {id: formParams.id, formParams, drawingParams}
}
async function submitForm() {
	loading.value = true
  let formContent = dealData()
  let formName = formContent.formParams.name + '.form'
	const params = { resName: formName, content: JSON.stringify(formContent)}
	if (id != undefined) {
		updateProcess(params).then(response => {
			loading.value = false
			proxy.$modal.msgSuccess("编辑成功")
			goBack()
		}).catch(() => loading.value = false)
	} else {
		addProcess(params).then(response => {
			loading.value = false
			proxy.$modal.msgSuccess("新增成功")
			goBack()
		}).catch(() => loading.value = false)
	}
}
function goBack() {
  const obj = { path: "/service/form", query: { t: Date.now(), current: route.query.current } }
  proxy.$tab.closeOpenPage(obj)
}

watch(() => activeData.value.label, (val, oldVal) => {
  if (
    activeData.value.placeholder === undefined
    || !activeData.value.tag
    || oldActiveId !== activeId.value
  ) {
    return
  }
  activeData.value.placeholder = activeData.value.placeholder.replace(oldVal, '') + val
})
watch(activeId, (val) => {
  oldActiveId = val
}, { immediate: true })

onMounted(() => {
  const clipboard = new ClipboardJS('#copyNode', {
    text: trigger => {
      const codeStr = generateCode()
      ElNotification({ title: '成功', message: '代码已复制到剪切板，可粘贴。', type: 'success' })
      return codeStr
    }
  })
  clipboard.on('error', e => {
    proxy.$modal.msgError('代码复制失败')
  })
  if(id) {
    getInfo()
  } else {
    formConf.value = JSON.parse(JSON.stringify(formConfData))
    drawingList.value = JSON.parse(JSON.stringify(drawingDefalut))
    activeData.value = drawingDefalut[0]
    activeId.value = drawingDefalut[0].formId
  }
})
</script>

<style lang="scss" scoped>
.form-container{
  margin-top: -24px;
  height: calc(100% + 24px);
  display: flex;
  border: 1px solid var(--tags-item-border, #E4E5E7);
  border-top: 0px;
  overflow: hidden;
  .left-board{
    width: 260px;
    height: 100%;
    border-right: 1px solid var(--tags-item-border, #E4E5E7);
    .left-header{
      height: 40px;
      line-height: 40px;
      text-align: center;
      border-bottom: 1px solid var(--tags-item-border, #E4E5E7);
    }
    .components-list{
      padding: 8px;
      .components-title{
        font-size: 14px;
        margin: 6px 2px;
        font-weight: 600;
        .svg-icon {
          font-size: 16px;
          margin-right: 5px;
        }
      }
      .components-draggable{
        padding-bottom: 20px;
        .components-item {
          display: inline-block;
          width: 48%;
          margin: 1%;
          transition: transform 0ms !important;
          .components-body {
            padding: 8px 10px;
            background: var(--el-color-primary-light-8);
            font-size: var(--el-font-size-extra-small);
            cursor: move;
            border: 1px dashed var(--el-color-primary-light-8);
            border-radius: 3px;
            .svg-icon {
              font-size: 15px;
              margin-right: 5px;
            }
            &:hover {
              border: 1px dashed var(--el-color-primary);
              color: var(--el-color-primary);
              .svg-icon {
                color: var(--el-color-primary);
              }
            }
          }
        }
      }
    }
  }
  .center-board{
    width: calc(100% - 350px - 260px);
    height: 100%;
    border-right: 1px solid var(--tags-item-border, #E4E5E7);
    .center-header{
      height: 40px;
      padding: 0 15px;
      border-bottom: 1px solid var(--tags-item-border, #E4E5E7);
      display: flex;
      align-items: center;
      justify-content: flex-end;
    }
    :deep(.drag-box){
      padding: 12px;
      background-color: var(--el-color-info-light-9);
      .el-form {
        padding: 12px;
        min-height: 50px;
        background-color: var(--el-bg-color);
        .drawing-board {
          height: 100%;
          position: relative;
          .components-body {
            padding: 0;
            margin: 0;
            font-size: 0;
          }
          .sortable-ghost {
            position: relative;
            display: block;
            overflow: hidden;
            &::before {
              content: " ";
              position: absolute;
              left: 0;
              right: 0;
              top: 0;
              height: 3px;
              background: var(--el-color-success);
              z-index: 2;
            }
          }
          .components-item.sortable-ghost {
            width: 100%;
            height: 60px;
            background: var(--el-color-primary-light-8);
          }
          .active-from-item {
            &>.el-form-item {
              background: var(--el-color-primary-light-8);
              border-radius: 6px;
            }
            &>.drawing-item-copy,
            &>.drawing-item-delete {
              display: initial;
            }
            &>.component-name {
              color: var(--el-color-primary);
            }
            .el-input__wrapper {
              box-shadow: 0 0 0 1px var(--el-input-hover-border-color) inset;
            }
          }
          .el-form-item {
            margin-bottom: 0px;
          }
        }
        .drawing-item {
          position: relative;
          cursor: move;
          &.unfocus-bordered:not(.activeFromItem)>div:first-child {
            border: 1px dashed #ccc;
          }
          .el-form-item {
            padding: 3px 10px 15px;
          }
        }
        .drawing-row-item {
          position: relative;
          cursor: move;
          box-sizing: border-box;
          border: 1px dashed #ccc;
          border-radius: 3px;
          padding: 0 2px;
          .drawing-row-item {
            margin-bottom: 2px;
          }
          .el-form-item {
            margin-bottom: 0;
          }
          .drag-wrapper {
            min-height: 60px;
            flex: 1;
            display: flex;
            flex-wrap: wrap;
            padding: 14px 0;
          }
          &.active-from-item {
            border: 1px dashed var(--el-color-primary);
          }
          .component-name {
            position: absolute;
            top: 0;
            left: 0;
            font-size: 12px;
            color: #bbb;
            display: inline-block;
            padding: 0 6px;
          }
        }
        .drawing-item, .drawing-row-item {
          &:hover {
            &>.el-form-item {
              background: var(--el-color-primary-light-8);
              border-radius: 6px;
            }
            &>.drawing-item-copy, &>.drawing-item-delete {
              display: initial;
            }
          }
          &>.drawing-item-copy, &>.drawing-item-delete {
            display: none;
            position: absolute;
            bottom: 0px;
            width: 22px;
            height: 22px;
            line-height: 22px;
            text-align: center;
            border-radius: 50%;
            font-size: 12px;
            border: 1px solid;
            cursor: pointer;
            z-index: 1;
          }
          &>.drawing-item-copy {
            right: 28px;
            border-color: var(--el-color-primary);
            color: var(--el-color-primary);
            background: #fff;
            &:hover {
              background: var(--el-color-primary);
              color: #fff;
            }
          }
          &>.drawing-item-delete {
            right: 0;
            border-color: #F56C6C;
            color: #F56C6C;
            background: #fff;
            &:hover {
              background: #F56C6C;
              color: #fff;
            }
          }
        }
        .empty-info {
          position: absolute;
          top: 46%;
          left: 0;
          right: 0;
          text-align: center;
          font-size: 18px;
          color: #ccb1ea;
          letter-spacing: 4px;
        }
      }
    }
  }
}
</style>